home *** CD-ROM | disk | FTP | other *** search
/ QRZ! Ham Radio 8 / QRZ Ham Radio Callsign Database - Volume 8.iso / mac / files / t_sys5 / unixcpio.gz / unixnet.cpio / dirutil.c < prev    next >
C/C++ Source or Header  |  1994-07-11  |  15KB  |  668 lines

  1. /* dirutil.c - MS-DOS directory reading routines
  2.  *
  3.  * Bdale Garbee, N3EUA, Dave Trulli, NN2Z, and Phil Karn, KA9Q
  4.  * Directory sorting by Mike Chepponis, K3MC
  5.  * adapted for ATARI ST & cleaned up by Rob Janssen, PE1CHL
  6.  * adapted for Lattice C by Walter Doerr, DG2KK
  7.  */
  8.  
  9. #include <stdio.h>
  10.  
  11. #ifndef LATTICE
  12. #include <ctype.h>        /* DG2KK: force Lattice to use library func. */
  13. #endif
  14.  
  15. #ifdef LATTICE
  16. #define    S_IJHID        2
  17. #define S_IJSYS        4
  18. #define S_IJDIR        0x10
  19. struct stat {
  20.     char st_mode;            /* that's all we need */
  21. };    
  22.  
  23. #else                        /* not LATTICE */
  24.  
  25. #ifdef __TURBOC__
  26. #include <dos.h>
  27. #include <sys\stat.h>
  28. #else                        /* not LATTICE and not TURBOC */
  29. #include <stat.h>
  30. #endif
  31. #endif
  32.  
  33. #ifdef MSDOS
  34. #define  IS_ERROR    == -1        /* error return from MSDOS */
  35. #endif
  36.  
  37. #ifdef __TURBOC__
  38. #define ST_RDONLY    0x01        /* Read only attribute */
  39. #define ST_HIDDEN    0x02        /* Hidden file */
  40. #define ST_SYSTEM    0x04        /* System file */
  41. #define ST_LABEL    0x08        /* Volume label */
  42. #define ST_DIRECT    0x10        /* Directory */
  43. #define ST_ARCH        0x20        /* Archive */
  44.  
  45. int dos(unsigned ah,
  46.     unsigned bx,
  47.     unsigned cx,
  48.     void *dx,
  49.     unsigned si,
  50.     unsigned di)
  51. {
  52.     union REGS regs;
  53.  
  54.     regs.h.ah = ah;
  55.     regs.x.bx = bx;
  56.     regs.x.cx = cx;
  57.     regs.x.dx = dx;
  58.     regs.x.si = si;
  59.     regs.x.di = di;
  60.  
  61.     intdos(®s, ®s);
  62.  
  63.     if (regs.x.cflag) return -1;
  64.     return 0;
  65. }
  66.  
  67. #define bdos(x, y)    bdos(x, (unsigned) y, 0)
  68.  
  69. #endif  /* TURBOC */
  70.  
  71.  
  72. #ifdef ATARI_ST
  73. #define  IS_ERROR    < 0        /* error return from gemdos */
  74. #include <osbind.h>            /* os interface defines */
  75. #define  bdos        gemdos        /* Atari OS call */
  76. #define  dos(a,b,c,d,e,f) gemdos(a,d,c) /* only valid for FIND func */
  77. #define  st_attr    st_mode     /* what's in a name? */
  78. #define  ST_HIDDEN    S_IJHID     /* Hidden from search */
  79. #define  ST_SYSTEM    S_IJSYS     /* System, hidden from search */
  80. #define  ST_DIRECT    S_IJDIR     /* Directory */
  81. #endif
  82.  
  83. #include "global.h"
  84.  
  85. #ifndef FALSE
  86. #define FALSE    (0)
  87. #endif
  88.  
  89. #ifndef TRUE
  90. #define TRUE    !(FALSE)
  91. #endif
  92.  
  93. #define REGFILE (ST_HIDDEN|ST_SYSTEM|ST_DIRECT)
  94. #define SET_DTA     0x1a
  95. #define FIND_FIRST    0x4e
  96. #define FIND_NEXT    0x4f
  97.  
  98. struct dirent {
  99.     char rsvd[21];
  100.     char attr;
  101.     short ftime;
  102.     short fdate;
  103.     long fsize;
  104.     char fname[13];
  105. };
  106. #define NULLENT (struct dirent *)0
  107.  
  108. struct dirsort {
  109.     struct dirsort *prev;
  110.     struct dirsort *next;
  111.     struct dirent *direntry;
  112. };
  113. #define NULLSORT (struct dirsort *)0
  114.  
  115. /* Create a directory listing in a temp file and return the resulting file
  116.  * descriptor. If full == 1, give a full listing; else return just a list
  117.  * of names.
  118.  */
  119. FILE *
  120. dir(path,full)
  121. char *path;
  122. int full;
  123. {
  124.     FILE *fp,*tmpfile();
  125.  
  126.     if ((fp = tmpfile()) != NULLFILE)
  127.     {
  128.         getdir(path,full,fp);
  129.         /* This should be rewind(), but Aztec doesn't have it */
  130. #if    (ATARI_ST && MWC)
  131.         vseek(fp,0L,0);
  132. #else
  133.         fseek(fp,0L,0);
  134. #endif
  135.     }
  136.     return fp;
  137. }
  138.  
  139. /* wildcard filename lookup */
  140. filedir(name,times,ret_str)
  141. char *name;
  142. int times;
  143. char *ret_str;
  144. {
  145.     register char *cp,*cp1;
  146.     static struct dirent sbuf;
  147.  
  148.     bdos(SET_DTA,&sbuf);    /* Set disk transfer address */
  149.  
  150.     /* Find matching file */
  151.     if(dos(times == 0 ? FIND_FIRST:FIND_NEXT,0,REGFILE,name,0,0) IS_ERROR) 
  152.         sbuf.fname[0] = '\0';
  153.  
  154.     /* Copy result to output, forcing to lower case */
  155.     for(cp = ret_str,cp1 = sbuf.fname; cp1 < &sbuf.fname[13] && *cp1 != '\0';)
  156.         *cp++ = (char)tolower(*cp1++);    /* DG2KK: added (char) */
  157.     *cp = '\0';
  158. }
  159.  
  160. /* Change working directory */
  161. docd(argc,argv)
  162. int argc;
  163. char *argv[];
  164. {
  165.     char dirname[128];
  166. #ifdef MSDOS
  167.     char *getcwd();
  168. #endif
  169.  
  170.     if(argc > 1){
  171.         if(chdir(argv[1]) IS_ERROR){
  172.             printf("Can't change directory\n");
  173.             return 1;
  174.         }
  175.     }
  176.  
  177. #ifdef MSDOS
  178.     if(getcwd(dirname,0) != NULLCHAR){
  179.         printf("\\%s\n",dirname);
  180.     }
  181. #endif
  182. #ifdef ATARI_ST
  183.     if (Dgetpath(dirname,0) == 0){
  184.         printf("%c:%s%s\n",(char) Dgetdrv()+'A',
  185.             (*dirname? "" : "\\"),dirname);
  186.     }
  187. #endif
  188.     return 0;
  189. }
  190.  
  191. /* List directory to console. [-/]w option selects "wide" format */
  192. dodir(argc,argv)
  193. int argc;
  194. char *argv[];
  195. {
  196.     char *path;
  197.     int full = 1;
  198.  
  199.     if (argc > 1 &&
  200.         (argv[1][0] == '-' || argv[1][0] == '/') && argv[1][1] == 'w')
  201.     {
  202.         full = -1;
  203.         argv++;
  204.         argc--;
  205.     }
  206.  
  207.     if(argc >= 2){
  208.         path = argv[1];
  209.     } else {
  210.         path = "*.*";
  211.     }
  212.     getdir(path,full,stdout);
  213.     return 0;
  214. }
  215.  
  216. /* do a directory list to the stream 
  217.  * full = 0 -> short form, 1 is long, -1 is multi-column short
  218. */
  219. static
  220. getdir(path,full,file)
  221. char *path;
  222. int full;
  223. FILE *file;
  224. {
  225.     struct dirent sbuf;
  226.     struct stat statbuf;
  227.     char *cp,*cp1;                /* !!!!!!! was: register */
  228.     char dirtmp[20];
  229.     int command = FIND_FIRST;
  230.     int i = 0;
  231.     int cflag = 0;
  232.     int n = 0;
  233.     char line_buf[50];        /* for long dirlist */
  234.     
  235.     struct dirsort *head, *here, *new;
  236.     struct dirent *de;
  237.     void dir_sort(), format_dir(), format_fname(), diskfree(), free_clist();
  238.     int malloc_lost = FALSE;
  239.  
  240.     /* Root directory is a special case */
  241.     if(path == NULLCHAR || *path == '\0' || strcmp(path,"\\") == 0)
  242.         path = "\\*.*";
  243.  
  244.     /* If arg is a directory, append "\*.*" to it.
  245.      * This is tricky, since the "stat" system call actually
  246.      * calls the DOS "find matching file" function. The stat
  247.      * call therefore returns the attributes for the first matching
  248.      * entry in the directory. If the arg already ends in *.*,
  249.      * stat will match the . entry in the directory and indicate
  250.      * that the argument is a valid directory name. Hence the
  251.      * heuristic check for '*' in the file name. Kludge...
  252.      */
  253. #ifdef __TURBOC__
  254.     else if(stat(path,&statbuf) != -1
  255.      && (statbuf.st_mode & S_IFDIR)
  256.      && index(path,'*') == NULLCHAR) {
  257. #else
  258.     else if(index(path,'*') == NULLCHAR
  259.      && stat(path,&statbuf) != -1
  260.      && (statbuf.st_attr & ST_DIRECT)) {
  261. #endif
  262.         if((cp = malloc(strlen(path) + 10)) == NULLCHAR)
  263.             return -1;
  264.         sprintf(cp,"%s%c%s",path,'\\',"*.*");
  265.         path = cp;
  266.         cflag = 1;
  267.     }
  268.     head = NULLSORT;    /* No head of chain yet... */
  269.     for(;;){
  270.         bdos(SET_DTA,&sbuf);    /* Set disk transfer address */
  271.         if(dos(command, 0, REGFILE, path, 0, 0) IS_ERROR)
  272.             break;
  273.  
  274.         command = FIND_NEXT;    /* Got first one already... */
  275.         if(sbuf.fname[0] != '.'){
  276.             /* nuke "." and ".." */
  277.             n++;    /* One more entry */
  278.             new = (struct dirsort *) malloc(sizeof(struct dirsort));
  279.             if(new == NULLSORT)
  280.                 malloc_lost = TRUE;
  281.             de  = (struct dirent *)malloc(sizeof(struct dirent));
  282.             if(de == NULLENT)
  283.                 malloc_lost = TRUE;
  284.             if(malloc_lost){
  285.                 /* Clean up and call other routine */
  286.                 if(new)free(new);
  287.                 free_clist(head);
  288.                 return getdir_nosort(path,full,file);
  289.             }
  290.             *de = sbuf;    /* Copy contents of directory entry struct */
  291.  
  292.             /* Fix up names for easier sorting... pain! */
  293.             strcpy(de->fname,"           ");    /* 11 blanks */
  294.             cp  = sbuf.fname;
  295.             cp1 = de->fname;
  296.  
  297.             do *cp1++ = *cp++; while (*cp && *cp != '.');
  298.  
  299.             if(*cp++){
  300.                 /* If there is an extension */
  301.                 cp1 = &(de->fname[8]);
  302.                 do *cp1++ = *cp++; while (*cp);
  303.             }
  304.             if(!(int)head){
  305.                 /* Make the first one */
  306.                 here = head = new;
  307.                 head->prev = head->next = NULLSORT;
  308.             } else {
  309.                 /* Link on next one */
  310.                 new->next = NULLSORT;
  311.                 new->prev = here;
  312.                 here->next = new;
  313.                 here = new;
  314.             }
  315.             new->direntry = de;
  316.         } /* IF on "." */
  317.     } /* infinite FOR loop */
  318.  
  319.     if(head)
  320.         dir_sort(head);     /* Make a nice, sorted list */
  321.  
  322.     here = head;
  323.     if(here)
  324.         if(full > 0){
  325.         do {
  326.             format_dir(line_buf,here->direntry);
  327.             fprintf(file,"%s%s",line_buf,(i^=1) ? "   " : "\r\n");
  328.         } while (here = here->next);
  329.         if(i & 1)
  330.             fprintf(file,"\r\n");
  331.         }
  332.         else {
  333.         /* This is the short form */
  334.         do {
  335.             format_fname(dirtmp,here->direntry->fname,
  336.                         here->direntry->attr);
  337.             fprintf(file,"%-15s%s",dirtmp,((full && ++i % 5)?"":"\r\n"));
  338.         } while (here = here->next);
  339.         if(full && i % 5)
  340.             fprintf(file,"\r\n");
  341.         }
  342.  
  343.     /* Give back all the memory we temporarily needed... */
  344.     free_clist(head);
  345.  
  346.     if(full > 0){
  347.         /* Provide additional information only on DIR */
  348.  
  349.         if (isalpha(*path) && path[1] == ':')    /* detect A: drivespec */
  350.             diskfree(file,*path & 0x1f,n);
  351.         else
  352.             diskfree(file,0,n);
  353.     }
  354.     if(cflag)
  355.         free(path);
  356.     return 0;
  357. } /* getdir */
  358.  
  359. static
  360. getdir_nosort(path,full,file)
  361. char *path;
  362. int full;
  363. FILE *file;
  364. {
  365.     struct dirent sbuf;
  366.     struct stat statbuf;
  367.     register char *cp;
  368.     char dirtmp[20];
  369.     int command = FIND_FIRST;
  370.     int i = 0;
  371.     int cflag = 0;
  372.  
  373.     char    line_buf[50];        /* for long dirlist */
  374.     
  375.     void format_fname(),format_dir(),diskfree();
  376.     int n = 0;    /* Number of directory entries */
  377.     
  378.     /* Root directory is a special case */
  379.     if(path == NULLCHAR || *path == '\0' || strcmp(path,"\\") == 0)
  380.         path = "\\*.*";
  381.  
  382.     /* If arg is a directory, append "\*.*" to it.
  383.      * This is tricky, since the "stat" system call actually
  384.      * calls the DOS "find matching file" function. The stat
  385.      * call therefore returns the attributes for the first matching
  386.      * entry in the directory. If the arg already ends in *.*,
  387.      * stat will match the . entry in the directory and indicate
  388.      * that the argument is a valid directory name. Hence the
  389.      * heuristic check for '*' in the file name. Kludge...
  390.      */
  391. #ifdef __TURBOC__
  392.     else if(stat(path,&statbuf) != -1
  393.      && (statbuf.st_mode & S_IFDIR)
  394.      && index(path,'*') == NULLCHAR) {
  395. #else
  396.     else if(index(path,'*') == NULLCHAR
  397.      && stat(path,&statbuf) != -1
  398.      && (statbuf.st_attr & ST_DIRECT)) {
  399. #endif
  400.         if((cp = malloc(strlen(path) + 10)) == NULLCHAR)
  401.             return -1;
  402.         sprintf(cp,"%s%c%s",path,'\\',"*.*");
  403.         path = cp;
  404.         cflag = 1;
  405.     }
  406.     for(;;){
  407.         bdos(SET_DTA,&sbuf);    /* Set disk transfer address */
  408.         if(dos(command,0,REGFILE,path,0,0) IS_ERROR)
  409.             break;
  410.  
  411.         command = FIND_NEXT;    /* Got first one already... */
  412.         if(sbuf.fname[0] != '.'){    /* nuke "." and ".." */
  413.             if(full > 0){
  414.                 n++;    /* Count 'em */
  415.                 format_dir(line_buf,&sbuf);
  416.                 fprintf(file,"%s%s",line_buf,(i^=1) ? "   " : "\n");
  417.             } else    {    /* is short form */
  418.                 format_fname(dirtmp,sbuf.fname,sbuf.attr);
  419.                 fprintf(file,"%-15s%s",dirtmp,(full && (++i % 5)?"":"\n"));
  420.             }
  421.         }
  422.     }
  423.     if(full > 0){
  424.         if(i)
  425.             fprintf(file,"\n");
  426.  
  427.         if (isalpha(*path) && path[1] == ':')    /* detect A: drivespec */
  428.             diskfree(file,*path & 0x1f,n);
  429.         else
  430.             diskfree(file,0,n);
  431.     }
  432.     else
  433.         if(i % 5)
  434.             fprintf(file,"\n");
  435.  
  436.     if(cflag)
  437.         free(path);
  438.     return 0;
  439. }
  440.  
  441. static
  442. void
  443. diskfree (file,drv,nfiles)
  444. FILE *file;
  445. int drv;
  446. int nfiles;
  447.  
  448. {
  449. #ifdef MSDOS
  450.     unsigned short ax,bx,cx,dx;
  451.     void isfree();
  452. #endif
  453. #ifdef ATARI_ST
  454.     struct { unsigned long di_free,di_many,di_ssize,di_spau; } disk_info;
  455. #endif
  456.     unsigned long free_bytes = 0, total_bytes = 0;
  457.     char s_free[11], s_total[11];
  458.     void commas();
  459.  
  460.     fflush(stdout);         /* function takes a short while... */
  461.  
  462. #ifdef MSDOS
  463.     /* Provide additional information  */
  464.     ax = 0x3600;    /* AH = 36h, AL = 0 (AL not used) */
  465.     dx = drv;    /* Default drive */
  466.     isfree(&ax,&bx,&cx,&dx);
  467.  
  468.     if (ax != 0xffff)
  469.     {
  470.         free_bytes  = (unsigned long)ax * (unsigned long)cx;
  471.         total_bytes = free_bytes * (unsigned long)dx;
  472.         free_bytes *= (unsigned long)bx;
  473.     }
  474. #endif
  475. #ifdef ATARI_ST
  476.     if (Dfree(&disk_info,drv) == 0)
  477.     {
  478.         free_bytes  = disk_info.di_spau * disk_info.di_ssize;
  479.         total_bytes = free_bytes * disk_info.di_many;
  480.         free_bytes *= disk_info.di_free;
  481.     }
  482. #endif
  483.  
  484.     sprintf(s_free,"%ld",free_bytes);    commas(s_free);
  485.     sprintf(s_total,"%ld",total_bytes);    commas(s_total);
  486.  
  487.     if(nfiles)
  488.         fprintf(file,"%d",nfiles);
  489.     else
  490.         fprintf(file,"No");
  491.  
  492.     fprintf(file," file%s. %s bytes free. Disk size %s bytes.\r\n",
  493.               (nfiles==1? "":"s"),s_free,s_total);
  494. }
  495.  
  496.  
  497. /*
  498.  * Return a string with commas every 3 positions.
  499.  * If malloc() fails, return original string unmodified.
  500.  * else the original string is replace with the string with commas.
  501.  *
  502.  * The caller must be sure that there is enough room for the resultant
  503.  * string.
  504.  *
  505.  *
  506.  * k3mc 4 Dec 87
  507.  * pe1chl 3 Feb 88     europeans would use periods...
  508.  */
  509.  
  510. #ifndef THSEP
  511. #define THSEP ','
  512. #endif
  513. void
  514. commas(dest)
  515. char *dest;
  516. {
  517.     char *src, *core;    /* Place holder for malloc */
  518.     unsigned cc;        /* The comma counter */
  519.     unsigned len;
  520.     
  521.     len = strlen(dest);
  522.     if( (core = src = (char *)malloc(len+1)) == NULLCHAR)
  523.         return;
  524.  
  525.     strcpy(src,dest);    /* Make a copy, so we can muck around */
  526.     cc = (len-1)%3 + 1;    /* Tells us when to insert a comma */
  527.     
  528.     while(*src != '\0'){
  529.         *dest++ = *src++;
  530.         if( ((--cc) == 0) && *src ){
  531.             *dest++ = THSEP; cc = 3;
  532.         }
  533.     }
  534.     free(core);
  535.     *dest = '\0';
  536. }
  537. /*
  538.  * This insertion sort adapted from "Writing Efficient Programs" by Jon Louis
  539.  * Bentley, Prentice-Hall 1982, ISBN 0-13-070244-X (paperback) p. 65
  540.  *
  541.  * Run Time (sec) = K * N^2, where K = 21e-6 on my turbo XT clone (SI=2.6).
  542.  * This could be improved to perhaps K * N * log2(N) using Quicksort, but,
  543.  * as Bentley points out, this insertion sort is actually faster for small
  544.  * values of N.  His "best" sorting algorithm uses an insertion sort/Quicksort
  545.  * hybrid, with the "cutoff" value being about 30 elements.
  546.  *
  547.  * I have opted for the straight insertion sort because it is quite simple,
  548.  * provably correct, and not a terrible performer when N < 1000, which is the
  549.  * case for most directories.
  550.  */
  551. static
  552. void
  553. dir_sort(head)
  554. struct dirsort *head;
  555. {
  556.     struct dirsort *here, *backtrack;
  557.     struct dirent *de_temp;
  558.     
  559.     for(here = head->next; here != NULLSORT; here = here->next){
  560.         backtrack = here;
  561.         de_temp = here->direntry;
  562.         while(backtrack->prev
  563.          && strcmp(de_temp->fname,backtrack->prev->direntry->fname)<0){
  564.             backtrack->direntry = backtrack->prev->direntry;
  565.             backtrack = backtrack->prev;
  566.         }
  567.         backtrack->direntry = de_temp;
  568.     }
  569. }
  570.  
  571. static
  572. void
  573. format_dir (line_buf,sbuf)
  574.     char *line_buf;
  575.     struct dirent *sbuf;
  576.  
  577. {
  578.     char dirtmp[20];
  579.     char cbuf[20],cbuf1[20];
  580.  
  581.     format_fname(dirtmp,sbuf->fname,sbuf->attr);
  582.  
  583.     sprintf(line_buf,"%-13s",dirtmp);
  584.     if(sbuf->attr & ST_DIRECT)
  585.         strcat(line_buf,"           ");/* 11 spaces */
  586.     else {
  587.         sprintf(cbuf,"%ld",sbuf->fsize);
  588.         commas(cbuf);
  589.         sprintf(cbuf1,"%10s ",cbuf);
  590.         strcat(line_buf,cbuf1); /* Do filesize */
  591.     }
  592.  
  593.     sprintf(cbuf,"%2d:%02d %2d/%02d/%02d",
  594.       (sbuf->ftime >> 11) & 0x1f,     /* hour */
  595.       (sbuf->ftime >> 5) & 0x3f,     /* minute */
  596.       (sbuf->fdate >> 5) & 0xf,     /* month */
  597.       (sbuf->fdate ) & 0x1f,     /* day */
  598.       (sbuf->fdate >> 9) + 80);     /* year */
  599.  
  600.     strcat(line_buf,cbuf);
  601. }
  602.  
  603. static
  604. void
  605. format_fname(dest,src,attr)
  606. char    *dest, *src;
  607. char    attr;
  608. {
  609.     char    *cp = src+8;
  610.     int    loop_counter;
  611.  
  612.     for(loop_counter=0; loop_counter<8; loop_counter++){
  613.         *dest++ = (char)tolower(*src++);    /* DG2KK:  (char) */
  614.         if(*src == ' ')break;
  615.     }
  616.     if(cp[0] != ' ' || cp[1] != ' ' || cp[2] != ' '){ /* There is an extension */
  617.         *dest++ = '.';
  618.         for(loop_counter=0; loop_counter<3; loop_counter++){
  619.         *dest++ = (char)tolower(*cp++);        /* DG2KK:  (char) */
  620.             if(*cp == ' ')break;
  621.         }
  622.     }
  623.     if(attr & ST_DIRECT)*dest++ = '\\';
  624.     *dest = '\0';
  625. }
  626.  
  627. static
  628. void
  629. free_clist(head)
  630. struct dirsort *head;
  631. {
  632.     struct dirsort *here;
  633.     
  634.     here = head;
  635.     if(here)do{
  636.         free(here->direntry);
  637.         if(head != here){
  638.             free(head);
  639.             head = here;
  640.         }
  641.     } while (here = here->next);
  642.     if(head != here){
  643.         free(head);
  644.         head = here;
  645.     }
  646. }
  647.  
  648. #ifdef LATTICE        /* DG2KK: Lattice doesn't know a thing about stat */
  649. int
  650. stat(name,buf)
  651. char *name;
  652. struct stat *buf;
  653. {
  654.     static struct dirent sbuf;
  655.     static struct stat statbuf;
  656.     int error;
  657.  
  658.     gemdos(SET_DTA, &sbuf);
  659.  
  660.     error = gemdos(FIND_FIRST, name, REGFILE);    /* look for directories */
  661.     if (error)
  662.         return(-1);            /* caller insists on -1 */
  663.  
  664.     statbuf.st_attr = sbuf.attr;
  665.     return 0;
  666. }
  667. #endif
  668.